package com.qilin.hammer.util;

import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class XMLUtil {

    public static String format(String xml) {
        final StringWriter sw;

        try {
            final OutputFormat format = OutputFormat.createPrettyPrint();
            format.setNewLineAfterDeclaration(false);
            final org.dom4j.Document document = DocumentHelper.parseText(xml);
            sw = new StringWriter();
            final XMLWriter writer = new XMLWriter(sw, format);
            writer.write(document);
        }
        catch (Exception e) {
            throw new RuntimeException("Error formating xml:\n" + xml, e);
        }
        return sw.toString();
    }

    /**
     * Returns true if Node n1 contains Node n2. Node n1 contains n2 if
     *  1) n1 and n2 have the same type
     *  2) n1 and n2 have the same name
     *  3) n1 and n2 have the same value, if any
     *  4) n1 and n2 have same set of attributes, if any
     *  5) the child nodes (if any) in n2 are all contained in some child
     *     nodes in n1. The child nodes needn't in the same order.
     *
     * This method is STRICTLY EXPERIMENTAL and therefore should NOT be 
     * used in production code without prior consent of the owner.
     */
    public static boolean checkContainDOM(Node n1, Node n2) {
        if (n1 == n2) {
            return true;
        }

        if (n1 == null) {
            return false;
        }

        if (n1.getNodeType() != n2.getNodeType()) {
            return false;
        }

        String n1name = n1.getNodeName();
        String n2name = n2.getNodeName();
        //ignore element prefix check
        if(n1name.contains(":")){
            n1name = n1name.substring(n1name.indexOf(':')+1);
        }
        if(n2name.contains(":")){
            n2name = n2name.substring(n2name.indexOf(':')+1);
        }
        //ignore root node name check
        Node rootNode = null;
        if (n1.getParentNode()!=null){
            rootNode = n1.getParentNode().getParentNode(); 
        }
        
        if (rootNode!=null && !n1name.equals(n2name)) {
            return false;
        }

        String n1val = n1.getNodeValue();
        String n2val = n2.getNodeValue();
        if (n1val != null) {
            if (n2val == null) {
                return false;
            }
            if (!n1val.trim().equals(n2val.trim())) {
                return false;
            }
        } else if (n2val != null) {
            return false;
        }
        
       /*// ignore attribute check
        if (n1.hasAttributes()) {
            if (n2.hasAttributes()) {
                NamedNodeMap n1attrs = n1.getAttributes();
                NamedNodeMap n2attrs = n2.getAttributes();
                if (n1attrs.getLength() < n2attrs.getLength()) {
                    return false;
                }
                Node attr1 = null;
                Node attr2 = null;
                for (int i = 0; i < n2attrs.getLength(); i++) {
                    attr2 = n2attrs.item(i);
                    attr1 = n1attrs.getNamedItem(attr2.getNodeName());
                    if (!checkContainDOM(attr1, attr2)) {
                        return false;
                    }
                }
            }
        } else if (n2.hasAttributes()) {
            return false;
        }*/
        
        if (n1.hasChildNodes()) {
            if (!n2.hasChildNodes()) {
                return false;
            }
            NodeList n1children = n1.getChildNodes();
            NodeList n2children = n2.getChildNodes();
            if (n1children.getLength() < n2children.getLength()) {
                return false;
            }
            nextn2:
            for (int j = 0; j < n2children.getLength(); j++) {
                for (int i = 0; i < n1children.getLength(); i++) {
                    if (checkContainDOM(n1children.item(i), n2children.item(j))) {
                        continue nextn2;
                    }
                }
                return false;
            }
        } else if (n2.hasChildNodes()) {
            return false;
        }

        return true;
    }

    public static boolean checkContainXML(String xml1, String xml2) {
        try {
            DocumentBuilderFactory domFactory =
                DocumentBuilderFactory.newInstance();
            DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
            InputSource is1 = new InputSource(new StringReader(xml1));
            Document doc1 = domBuilder.parse(is1);
            InputSource is2 = new InputSource(new StringReader(xml2));
            Document doc2 = domBuilder.parse(is2);
            return checkContainDOM(doc1, doc2);
        } catch (Exception e) {
            //Logger.log(e);
            return false;
        }
    }

}
